﻿using System;
using System.Collections.Generic;
using System.Data;

namespace Finance.Data.Entities
{
	internal class Documents : DataBaseObjectCollection<IDocument>, IDocuments
	{
		private const string InsertDocumentQuery =
			"insert into document (docdatetime, amount, typecode, documentsubtypeid, Description, contractorId, documentid) " +
			"values (@p0, @p1, @p2, @p3, @p4, @p5, @p6)";

		private const string UpdateDocumentQuery =
			"update document set amount = @p0, documentsubtypeid = @p1, description = @p2, contractorId = @p3, docdatetime = @p4 where documentid = @p5";

		private const string RetrieveDocumentsQuery =
			"select documentid, docdatetime, amount, Description, name, document.typecode, contractorId " +
			"from document left join documentsubtype on documentsubtype.documentsubtypeid = document.documentsubtypeid " +
			"where docdatetime >=@p0 and docdatetime < @p1 " +
			"order by docdatetime";

		public Documents(FinanceDataBase dataBase) : base(dataBase, "document") {}

		public override IDocument GetItem(Guid guid)
		{
			using (var dataReader = DataBase.Sql.ExecuteReader(
				"select document.*, documentsubtype.name as name from document inner join " +
				"documentsubtype on documentsubtype.documentsubtypeid = document.documentsubtypeid " +
				"where documentid = @p0", guid))
			{
				if(!dataReader.Read())
					throw new Exception("The item not found.");

				return ParseItem(dataReader);
			}
		}

		public override IDocument NewItem()
		{
			return new Document(this, FinanceDataBase.Contractors);
		}

		public IList<IDocument> RetrieveDocuments(DateTime date)
		{
			var dateStart = new DateTime(date.Year, date.Month, date.Day);
			var dateEnd = dateStart.AddDays(1);

			using (var dataReader = DataBase.Sql.ExecuteReader(RetrieveDocumentsQuery, dateStart, dateEnd))
			{
				var documents = new List<IDocument>();
				while (dataReader.Read())
				{
					var document = ParseItem(dataReader);

					documents.Add(document);
				}
				return documents;
			}
		}

		protected override IDocument ParseItem(IDataRecord dataReader)
		{
			var document = new Document(this, FinanceDataBase.Contractors)
				{
					Description = (string) dataReader["Description"],
					Id = (Guid) dataReader["documentid"],
					DocDate = (DateTime) dataReader["docdatetime"],
					Amount = (decimal) dataReader["amount"],
					SubTypeName = (string) dataReader["name"],
					DocumentType = (DocumentType)(int) dataReader["typecode"],
					ContractorId = GetGuid(dataReader,"contractorid")
				};
			return document;
		}

		FinanceDataBase FinanceDataBase
		{
			get { return (FinanceDataBase) DataBase; }
		}

		protected override void Update(IDocument document)
		{
			Guid subTypeGuid = FinanceDataBase.DocumentSubTypes.CheckSubType(
				document.DocumentType, document.SubTypeName);

			try
			{
				using (var sql = ((FinanceDataBase) DataBase).OpenCommandProcessor())
				{
					sql.TransactionBegin();
					sql.ExecuteNonQuery(
						UpdateDocumentQuery,
						document.Amount,
						subTypeGuid,
						document.Description ?? "",
						document.Contractor == null ? Guid.Empty : document.Contractor.Id,
						document.DocDate,
						document.Id);
					sql.TransactionCommit();
				}
			}
			catch (Exception exception)
			{
				throw new Exception("Ошибка обновления данных документа в базе", exception);
			}
		}

		protected override void Insert(IDocument document)
		{
			var documentSubTypeId = FinanceDataBase.DocumentSubTypes.CheckSubType(
				document.DocumentType, document.SubTypeName);
            
			try
			{
				using (var sql = ((FinanceDataBase) DataBase).OpenCommandProcessor())
				{
					sql.TransactionBegin();
					document.Id = Guid.NewGuid();
					sql.ExecuteNonQuery(InsertDocumentQuery, 
						document.DocDate,
						document.Amount,
						(int)document.DocumentType,
						documentSubTypeId,
						document.Description ?? "",
						document.ContractorId,
						document.Id);
					sql.TransactionCommit();
				}
			}
			catch (Exception exception)
			{
				throw new Exception("Ошибка сохранения данных документа в базе", exception);
			}
		}

		public override void Save(object item)
		{
			Save((Document)item);
		}
	}
}
